﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

using GeneralTool.CoreLibrary.Interfaces;
using GeneralTool.CoreLibrary.Logs;


namespace GeneralTool.CoreLibrary.TaskLib
{
    #region New

    /// <summary>
    /// 远程proxy package客户端
    /// </summary>
    public static class PackageClientBuilder
    {
        /// <summary>
        /// 创建一个远程服务接口,该远程服务连接的是 <see cref="PackageStationClient"/>
        /// </summary>
        /// <typeparam name="T">要创建的远程服务的接口(不需要有实现)</typeparam>
        /// <param name="ip">远程服务IP</param>
        /// <param name="port">远程服务Port</param>
        /// <param name="url">远程服务的类Url,全名称,如有/需要写入</param>
        /// <param name="log">日志组件</param>
        /// <param name="jsonConverter">json转换器组件</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="packageHelper">数据转换器组件</param>
        /// <param name="timeOutMillSeconds">超时时间</param>
        /// <returns>代理实现类</returns>
        /// <exception cref="Exception"></exception>
        public static T CreateChannel<T>(string ip,
            int port,
            string url,
            ILog log = null,
            IJsonConvert jsonConverter = null,
            Encoding encoding = null,
            PackageHelper packageHelper = null,
            int timeOutMillSeconds = 0)
            where T : class
        {
            var type = typeof(T);
            if (!type.IsInterface)
                throw new Exception($"{nameof(T)} 必须是一个接口");

            if (log == null) log = new ConsoleLogInfo();
            if (jsonConverter == null) jsonConverter = new BaseJsonCovert();
            if (encoding == null) encoding = Encoding.UTF8;
            if (packageHelper == null) packageHelper = new PackageHelper(jsonConverter, encoding);

            return (T)CreateWrap(type, ip, port, url, log, jsonConverter, encoding, packageHelper, timeOutMillSeconds);
        }

        private static object CreateWrap(Type type,
            string ip,
            int port,
            string url,
            ILog log = null,
            IJsonConvert jsonConverter = null,
            Encoding encoding = null,
            PackageHelper packageHelper = null,
            int timeOutMillSeconds = 0)
        {
            var assemblyName = new AssemblyName($"DynamicAssembly_[{type.Assembly.GetName().Name}]");
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule($"DynamicModule_{type.Module.Name}");

            var typeBuilder = moduleBuilder.DefineType($"DynamicClass_{type.Name}", TypeAttributes.Public);
            if (type.IsInterface)
                typeBuilder.AddInterfaceImplementation(type);

            //定义私有字段
            var logFieldBuilder = typeBuilder.DefineField("log", typeof(ILog), FieldAttributes.Private);
            var jsonConverterBuilder = typeBuilder.DefineField("jsonConverter", typeof(IJsonConvert), FieldAttributes.Private);
            var encodingBuilder = typeBuilder.DefineField("encoding", typeof(Encoding), FieldAttributes.Private);
            var packageHelperBuilder = typeBuilder.DefineField("packageHelper", typeof(PackageHelper), FieldAttributes.Private);
            var urlBuilder = typeBuilder.DefineField("url", typeof(string), FieldAttributes.Private);
            var timeOutBuilder = typeBuilder.DefineField("timeOutMillSeconds", typeof(int), FieldAttributes.Private);

            //定义，一个构造函数
            var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public,
                 CallingConventions.Standard,
                 new Type[] { typeof(ILog), typeof(IJsonConvert), typeof(Encoding), typeof(PackageHelper), typeof(string), typeof(int) });

            var constructorIL = constructorBuilder.GetILGenerator();
            // 调用基类的构造函数
            constructorIL.Emit(OpCodes.Ldarg_0);
            ConstructorInfo baseConstructor = typeof(object).GetConstructor(Type.EmptyTypes);
            constructorIL.Emit(OpCodes.Call, baseConstructor);

            // 将参数赋值给字段
            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_1);
            constructorIL.Emit(OpCodes.Stfld, logFieldBuilder);

            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_2);
            constructorIL.Emit(OpCodes.Stfld, jsonConverterBuilder);

            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_3);
            constructorIL.Emit(OpCodes.Stfld, encodingBuilder);

            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_S, (byte)4);
            constructorIL.Emit(OpCodes.Stfld, packageHelperBuilder);

            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_S, (byte)5);
            constructorIL.Emit(OpCodes.Stfld, urlBuilder);

            constructorIL.Emit(OpCodes.Ldarg_0);
            constructorIL.Emit(OpCodes.Ldarg_S, (byte)6);
            constructorIL.Emit(OpCodes.Stfld, timeOutBuilder);
            // 返回
            constructorIL.Emit(OpCodes.Ret);

            var methods = type.GetMethods();
            foreach (var method in methods)
            {
                ImplementMethod(typeBuilder, method, ip, port, logFieldBuilder, jsonConverterBuilder, encodingBuilder, packageHelperBuilder, urlBuilder, timeOutBuilder);
            }
            var dynamicType = typeBuilder.CreateType();
            return Activator.CreateInstance(dynamicType, log, jsonConverter, encoding, packageHelper, url, timeOutMillSeconds);
        }


        private static void ImplementMethod(TypeBuilder typeBuilder,
            MethodInfo method,
            string ip,
            int port,
            FieldBuilder logFieldBuilder,
            FieldBuilder jsonConverterBuilder,
            FieldBuilder encodingBuilder,
            FieldBuilder packageHelperBuilder,
            FieldBuilder urlBuilder,
            FieldBuilder timeOutBuilder)
        {
            var parameters = method.GetParameters();

            var methodBuilder = typeBuilder.DefineMethod(
                method.Name,
                MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.HideBySig,
                method.ReturnType,
                parameters.Select(p => p.ParameterType).ToArray());

            var methodIL = methodBuilder.GetILGenerator();

            // 加载 log.Debug 所需参数
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, logFieldBuilder);
            methodIL.Emit(OpCodes.Ldstr, $"Calling {method.Name} with IP: {ip}, Port: {port}, ReturnType: {method.ReturnType.FullName} and args:");
            methodIL.EmitCall(OpCodes.Callvirt, typeof(ILog).GetMethod("Debug", new Type[] { typeof(string) }), null);

            // 创建 PackageStationClient 实例
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, logFieldBuilder);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, jsonConverterBuilder);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, encodingBuilder);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, packageHelperBuilder);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, timeOutBuilder);
            methodIL.Emit(OpCodes.Newobj, typeof(PackageStationClient).GetConstructor(new Type[] { typeof(ILog), typeof(IJsonConvert), typeof(Encoding), typeof(PackageHelper), typeof(int) }));

            // 加载 Invoke 方法所需参数
            methodIL.Emit(OpCodes.Ldstr, ip);
            methodIL.Emit(OpCodes.Ldc_I4, port);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, urlBuilder);
            methodIL.Emit(OpCodes.Ldstr, method.Name);

            // 创建参数字典
            methodIL.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(Type.EmptyTypes));

            for (int i = 0; i < parameters.Length; i++)
            {
                methodIL.Emit(OpCodes.Dup);
                methodIL.Emit(OpCodes.Ldstr, parameters[i].Name);
                methodIL.Emit(OpCodes.Ldarg, i + 1);
                if (parameters[i].ParameterType.IsValueType)
                {
                    methodIL.Emit(OpCodes.Box, parameters[i].ParameterType);
                }
                methodIL.EmitCall(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add", new Type[] { typeof(string), typeof(object) }), null);
            }

            methodIL.Emit(OpCodes.Ldtoken, method.ReturnType);
            methodIL.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }), null);

            // 调用 Invoke 方法 string ip, int port, string url, string method, Dictionary<string, object> args, Type returnType
            methodIL.EmitCall(OpCodes.Callvirt, typeof(PackageStationClient).GetMethod(nameof(PackageStationClient.Invoke), new Type[] { typeof(string), typeof(int), typeof(string), typeof(string), typeof(Dictionary<string, object>), typeof(Type) }), null);

            // 处理返回值
            if (method.ReturnType == typeof(void))
            {
                methodIL.Emit(OpCodes.Pop); // 弹出返回值（如果有）
                methodIL.Emit(OpCodes.Ret);
            }
            else
            {
                // 直接返回 object 类型，由调用者进行类型转换
                if (method.ReturnType != typeof(object))
                {
                    // 如果接口方法返回类型不是 object，需要根据情况处理
                    if (method.ReturnType.IsValueType)
                    {
                        // 对于值类型，需要拆箱
                        methodIL.Emit(OpCodes.Unbox_Any, method.ReturnType);
                    }
                    else
                    {
                        // 对于引用类型，直接转换
                        methodIL.Emit(OpCodes.Castclass, method.ReturnType);
                    }
                }
                methodIL.Emit(OpCodes.Ret);
            }
        }
    }

    #endregion
}
